#!/usr/bin/env python
#program imake
# GuanLei(guan52@gmail.com)
import os,sys,platform,copy

class imake_args:
	def __init__(self,a,b):
		self.indent = a
		self.debug = b


global s_paths;
global s_env_paths;
global imake_stack

imake_stack = [ imake_args('',False) ]

s_paths=[]
s_env_paths=[]

def find_path(parent_fname, fname):
	if os.path.exists(fname):
		return fname

	sss = []
	sss.append ( os.path.dirname(parent_fname) )
	sss += s_paths
	sss += s_env_paths
	for i in sss:
		s = os.path.join(i, fname)
		if os.path.exists(s):
			return s
	return None



imake_header='''#!/usr/bin/env python
import sys, os, shutil

class oo_output:
	def __init__(self):
		self.s_filename = '%(name)s'
		self.s_file = open ('%(name)s.tmp','w')
		self.s_lines = open ('%(name)s','r').readlines()
		return

	def close(self):
		self.s_file.close()
		self.s_file = None
		#shutil.copyfile('%(name)s', '%(name)s.bak')
		shutil.copyfile('%(name)s.tmp', '%(name)s')
		os.remove('%(name)s.tmp')
		%(indent_flag)s
		return

	def write_lines(self,_beg,_end):
		while _beg < _end:
			self.s_file.write(self.s_lines[_beg])
			_beg += 1
		return

	def write(self,s):
		self.s_file.write(s)
		return

	def auto_begin(self):
		self.s_file.write('%(autogen_begin)s\\n')
		return

	def auto_end(self):
		self.s_file.write('%(autogen_end)s\\n')
		return

def inc(fname,m):
	lines = open(fname,'r').readlines()
	for i in lines:
		oo.write ( i %% m )
	return

global oo

def make():
	global oo
	oo = oo_output()
	sys.path.append('%(tool_path)s')

'''

imake_end='''
	oo.close()
'''


class imake_conf(list):
	def open_file(self,nm):
		lines = open(nm,'r').readlines()
		self.open_list (lines, os.path.dirname(nm))

	def open_list(self,lines,root):

		for i in lines:
			s = i.strip('\r\n\t ').split('#',1)[0].strip('\t ')

			if len(s) == 0:
				continue

			if s.startswith('-'):
				self.append(s)
				continue
			if root:
				f_path = os.path.join(root, s)
			else:
				f_path = s

			if not os.path.exists(f_path):
				raise RuntimeError('imake.conf:%s not found\n' % f_path)

			if os.path.isdir(f_path):
				f = imake_conf() 
				f.open_file(os.path.join (f_path,'imake.conf') )
				self.append('--push')
				for tt in f:
					self.append(tt)
				self.append('--pop')
			else:
				self.append(f_path)

	def process(self):
		global imake_stack

		for i in self:
			if i=='--push':
				imake_stack.insert(0, copy.copy(imake_stack[0]) )
			elif i=='--pop':
				if len(imake_stack) == 1:
					raise RuntimeError('--pop mismatch')
				imake_stack.pop()
			elif i=='--debug':
				imake_stack[0].debug = True
			elif i=='--no-debug':
				imake_stack[0].debug = False
			elif i.startswith('--indent'):
				imake_stack[0].indent = i[8:].strip()
			elif i.startswith('--'):
				raise RuntimeError('unknown command [%s]' % i)
			else:
				process_gen (i)
				
		return True

def process_gen(fname):
	global imake_stack
	print "imake %s\n" % fname
	
	name_path = os.path.dirname(fname)
	name_no_ext,ext = os.path.splitext(fname)
	py_name = os.path.join ( name_path, os.path.basename(fname).replace('.','_') )
	
	ext = ext.lower()
	name_base = os.path.basename(name_no_ext)

	
	if name_path=='':
		name_fullpath = os.getcwd()
	else:
		name_fullpath = os.path.abspath(name_path)

	
	print 'full-path :%s' % name_fullpath
	print 'py_name   :%s' % py_name
	print 'base-name :%s' % name_base
	
	f = open(py_name + '.py' ,'w')
	
	if ext in ['.h', '.c', '.hxx', '.cxx', '.cpp','.inl']:
		gg = imake_gen_c (f, fname)
	elif ext in ['.html', '.htm', '.xml']:
		gg = imake_gen_html (f, fname)
	elif (ext in ['.sh', '.bash']) or name_base=='makefile':
		gg = imake_gen_sh(f, fname)
	else:
		f.close()
		raise RuntimeError('unknown format %s\n' % fname)
		try:
			os.remove(py_name + '.py')
		except:
			pass		
		return

	gg.gen()
	f.close()
	
	old_cc = os.getcwd()
	os.chdir( name_fullpath)

	sys.path.insert(0, name_fullpath)
	vMod = __import__(os.path.basename(py_name))
	del sys.path[0]
	vMod.make()
	del vMod
	os.chdir(old_cc)
	
	try:
		os.remove(py_name + '.pyc')
	except:
		pass
	if not imake_stack[0].debug:
		os.remove(py_name + '.py' )
	return True


class segment:
	def __init__(self, begnum, endnum, ty, pp):
		self._type = ty
		self._begin = begnum
		self._end   = endnum
		self._line  = pp
		return

	def write_autogen(self,f,lines):
		f.write('	oo.auto_begin()\n')
		self.write_code(f,lines)
		f.write('	oo.auto_end()\n')
		return

	def write_code(self,f,lines):
		if self._begin + 1 == self._end:
			[cmd,param] = self.get_cmd()
			f.write('	%s\n' % param)
		else:
			i = self._begin + 1
			while i < (self._end - 1):
				ll = lines[i]
				i += 1
				f.write('	%s' % ll)
			return

class segment_c(segment):
	def get_cmd(self):
		if not self._line.startswith('/*--IMAKE'):
			return [None,None]
		l = self._line[4:]
		if l.endswith('*/'):
			l = l[0:len(l) - 2].strip(' ')
		
		nn = l.find(' ')
		if nn > 0:
			cmd=l[0:nn+1].strip(' ')
			param=l[nn+1:].strip(' ')
		else:
			cmd=l
			param=''
		return [cmd,param]
	
class segment_sh(segment):
	def get_cmd(self):
		if not self._line.startswith('#--IMAKE'):
			return [None,None]
		l = self._line[3:].strip(' ')
		
		nn = l.find(' ')
		if nn > 0:
			cmd=l[0:nn+1].strip(' ')
			param=l[nn+1:].strip(' ')
		else:
			cmd=l
			param=''
		return [cmd,param]

	def write_code(self,f,lines):
		if self._begin + 1 == self._end:
			[cmd,param] = self.get_cmd()
			f.write('	%s\n' % param)
		else:
			i = self._begin
			while i < self._end:
				ll = lines[i]
				if ll.startswith('#--IMAKE '):
					ll = ll[8:] + '\n'
				else:
					ll = ll[1:]
				i += 1
				f.write('	%s' % ll)
			return

class segment_html(segment):
	def get_cmd(self):
		if not self._line.startswith('<!--IMAKE'):
			return [None,None]
		l = self._line[4:].strip('\r\n\t ')
		if l.endswith('-->'):
			l = l[0:len(l)-3].strip('\r\n\t ')
		
		nn = l.find(' ')
		if nn > 0:
			cmd=l[0:nn+1].strip(' ')
			param=l[nn+1:].strip(' ')
		else:
			cmd=l
			param=''
		return [cmd,param]

class imake_file_base(list):
	def open_file(self,fname):
		self._fname = fname		
		self.s_lines = []
		f = open (fname,'r')
		while True:
			rrr = f.readline()
			if not rrr:
				break
			self.s_lines.append(rrr.strip('\r\n ').replace('\t',' '))
		f.close()
		self.parse()

	def find_startswith(self,fr,s):
		while fr < len(self.s_lines):
			l = self.s_lines[fr].strip()
			if l.startswith(s):
				return fr
			fr += 1
		return -1

	def find_endswith(self,fr,s):
		while fr < len(self.s_lines):
			l = self.s_lines[fr].strip()
			if l.endswith(s):
				return fr
			fr += 1
		return -1

	def find_not_startswith(self,fr,s):
		while fr < len(self.s_lines):
			l = self.s_lines[fr].strip()
			if not l.startswith(s):
				return fr
			fr += 1
		return -1

class imake_c_file(imake_file_base):
	def parse(self):
		i = 0
		while i < len(self.s_lines):
			imake_i = self.find_startswith(i,'/*--IMAKE')
			if imake_i < 0:
				self.append(segment_c(i, len(self.s_lines), 'code', ''))
				break
			if i < imake_i:
				self.append(segment_c(i, imake_i, 'code', ''))
			end_i = self.find_endswith(imake_i, '*/')
			if end_i < 0:
				raise RuntimeError('*/ not found')
			self.append(segment_c(imake_i, end_i + 1, 'imake', self.s_lines[imake_i]))
			i = end_i + 1				

class imake_html_file(imake_file_base):
	def parse(self):
		i = 0
		while i < len(self.s_lines):
			imake_i = self.find_startswith(i,'<!--IMAKE')
			if imake_i < 0:
				self.append(segment_html(i, len(self.s_lines), 'code', ''))
				break
			if i < imake_i:
				self.append(segment_html(i, imake_i, 'code', ''))
			end_i = self.find_endswith(imake_i, '-->')
			if end_i < 0:
				raise RuntimeError('--> not found')
			self.append(segment_html(imake_i, end_i + 1, 'imake', self.s_lines[imake_i]))
			i = end_i + 1				

class imake_sh_file(imake_file_base):
	def parse(self):
		i = 0
		while i < len(self.s_lines):
			imake_i = self.find_startswith(i,'#--IMAKE')
			if imake_i < 0:
				self.append(segment_sh(i, len(self.s_lines), 'code', ''))
				break
			if i < imake_i:
				self.append(segment_sh(i, imake_i, 'code', ''))
			
			end_i = self.find_end(imake_i)
			self.append(segment_sh(imake_i, end_i + 1, 'imake', self.s_lines[imake_i]))
			i = end_i + 1				

	def find_end(self,beg):
		while beg < len(self.s_lines):
			l = self.s_lines[beg].strip()
			if not l.startswith('#'):
				return beg
			if l.startswith('#--IMAKE-'):
				return beg
			beg += 1
		return len(self.s_lines)

class imake_gen_base:
	def __init__(self, f, fname, reader, gen_beg, gen_end):
		self._output = f
		self._fname = fname
		self._include_path = []
		self._reader = reader
		self._gen_beg = gen_beg
		self._gen_end = gen_end
		return

	def gen_include(self,fname):
		basename,ext = os.path.splitext(fname)
		if ext =='':
			fname += '.py'
		elif ext != '.py':
			raise RuntimeError('include must be python lib')
		
		v_fname = find_path (self._fname, fname)
		if not v_fname:
			raise RuntimeError('%s not found\n' % fname)
		
		lib_name,ext = os.path.splitext( os.path.basename(v_fname) )
		lib_path = os.path.dirname(v_fname)
		print 'include:%s(%s)\n' % (lib_name, lib_path)
		if not ( lib_path in self._include_path):
			self._include_path.append(lib_path)
			self._output.write('	sys.path.append(\'%s\')\n' % lib_path.replace('\\','\\\\') )
		self._output.write('	import %s\n	%s.set_oo(oo)\n' % (lib_name,lib_name) )

	def gen(self):
		global imake_stack
		
		self._reader.open_file(self._fname)
		
		tool_path = os.path.dirname(sys.argv[0])
		if tool_path == '':
			tool_path = os.get_cwd()
		self._include_path.append(tool_path)
		
		if imake_stack[0].indent == '':
			indent_flag=''
		else:
			indent_flag='os.system(\"%s\")' % ( imake_stack[0].indent + ' ' + os.path.basename(self._fname) )

		param = {
			'name':os.path.basename(self._fname),
			'indent_flag':indent_flag,
			'tool_path':tool_path.replace('\\','\\\\'),
			'autogen_begin':self._gen_beg,
			'autogen_end':self._gen_end,
		}
		self._output.write(imake_header % param)

		f = self._output
		f_delete = False

		for seg in self._reader:
			if seg._type == 'code' and (not f_delete):
				self._output.write('	oo.write_lines(%d,%d)\n' % (seg._begin, seg._end) )
				continue
			[cmd,pp] = seg.get_cmd()
			if cmd=='IMAKE-AUTOGEN-BEGIN':
				f_delete = True
				continue
			elif cmd=='IMAKE-AUTOGEN-END':
				f_delete = False
				continue
			if f_delete:
				continue

			if cmd != 'IMAKE-ONCE':
				self._output.write('	oo.write_lines(%d,%d)\n' % (seg._begin, seg._end) )

			if cmd=='IMAKE-INCLUDE':
				self.gen_include(pp);
			elif cmd=='IMAKE':
				seg.write_autogen(f, self._reader.s_lines)
			elif cmd=='IMAKE-ONCE':
				seg.write_code(f, self._reader.s_lines)

		f.write( imake_end )
		return 0;

class imake_gen_c(imake_gen_base):
	def __init__(self,f,fname):
		imake_gen_base.__init__(self,f,fname,imake_c_file(),'/*--IMAKE-AUTOGEN-BEGIN*/','/*--IMAKE-AUTOGEN-END*/')
		return

class imake_gen_html(imake_gen_base):
	def __init__(self,f,fname):
		imake_gen_base.__init__(self,f,fname,imake_html_file(),'<!--IMAKE-AUTOGEN-BEGIN-->','<!--IMAKE-AUTOGEN-END-->')
		return

class imake_gen_sh(imake_gen_base):
	def __init__(self,f,fname):
		imake_gen_base.__init__(self,f,fname,imake_sh_file(),'#--IMAKE-AUTOGEN-BEGIN','#--IMAKE-AUTOGEN-END')
		return

def scan_file(fm,tag):
	f = open(fm,'r')
	Found = False
	while not Found:
		r = f.readline()
		if not r:
			break
		Found = r.find(tag) >= 0
	f.close()
	return Found
		

def imake_show_help():
	sys.exit(0)

def imake_scan_path_r(root,rep):
	files = os.listdir(root)
	r_list = []
	for i in files:
		f_n = os.path.join(root,i)
		s_name,s_ext = os.path.splitext(i)
		s_ext = s_ext.lower()
		if s_ext in ['.c', '.h', '.cpp', '.inl']:
			if scan_file(f_n,'/*--IMAKE'):
				r_list.append(rep+i)
		elif s_ext in ['.html', '.htm', '.xml']:
			if scan_file(f_n,'<!--IMAKE'):
				r_list.append(rep+i)
		elif s_ext in ['.sh', '.bash'] or s_name == 'makefile':
			if scan_file(f_n,'#--IMAKE'):
				r_list.append(rep+i)
		elif os.path.isdir(f_n):
			if rep == '':
				n_rep = '%s/' % i
			else:
				n_rep = '%s%s/' % (rep, i)
			r = imake_scan_path_r(f_n,n_rep)
			for ttt in r:
				r_list.append(ttt)
	return r_list

def imake_scan_path(root):
	r_list = imake_scan_path_r(root,'')
	
	#print 'list %d\n' % len(r_list)
	#for i in r_list:
	#	print '%s\n' % i

	imake_fm = os.path.join(root, 'imake.conf')
	conf_list = []
	if os.path.exists(imake_fm):
		f = open(imake_fm,'r')
		while True:
			r = f.readline()
			if not r:
				break
			conf_list.append(r.strip('\r\n '))
		f.close()

	for i in r_list:
		if not ( i in conf_list):
			conf_list.append(i)

	f = open(imake_fm,'w')
	for i in conf_list:
		if i.startswith('-') or i.startswith('#') or ( i in r_list) or os.path.isdir( os.path.join(root,i) ):
			f.write('%s\n' % i)
	f.close()
	sys.exit(0)

def imake_install():
	sys.exit(0)

def imake_all(path):
	paths = find_file(path, 4, 'imake.conf')
	
	old_dir = os.getcwd()
	
	for i in paths:
		print 'enter %s' % i
		os.chdir(i)
		conf = imake_conf()
		conf.open_list('.', None)
		for tt in conf:
			print "-- %s\n" % tt
		conf.process()		
		os.chdir(old_dir)
		print 'leave %s' % i

def imake_create_vidl(files):
	if len(files) == 0:
		print 'please specify the idl files or path'
		sys.exit(1)
	
	f = []
	args = {}
	for i in files:
		if i.startswith('--'):
			k = i.split('=')
			if len(k)==1:
				args[k[0]] = True
			elif len(k)==2:
				args[k[0]] = k[1]
		elif os.path.isdir(i):
			for s in os.listdir(i):
				if s.endswith('.idl'):
					f.append ( os.path.join (i, s) )
		else:
			f.append(i)
	
	vidl = __import__('VIDL')	
	for i in f:
		vidl.create_by_idl(i, args)
	
	del vidl
	return 

def imake_create_impl(files):
	if len(files) == 0:
		print 'please specify the idl files or path'
		sys.exit(1)
	
	f = []
	args = {}
	for i in files:
		if i.startswith('--'):
			k = i.split('=')
			if len(k)==1:
				args[k[0]] = True
			elif len(k)==2:
				args[k[0]] = k[1]
		elif os.path.isdir(i):
			for s in os.listdir(i):
				if s.endswith('.idl'):
					f.append ( os.path.join (i, s) )
		else:
			f.append(i)
	
	vidl = __import__('VIDL')	
	for i in f:
		vidl.create_impl(i, args)
	
	del vidl
	return 

def find_file(path,dep,fileName):
	stack = []
	stack.append( (path,dep) )
	r = []
	while len(stack) > 0:
		path,dep = stack[0]
		del stack[0]

		if os.path.exists( os.path.join(path,fileName) ):
			r.append(path)
			continue

		if dep == 0:
			continue

		files = os.listdir(path)
		for i in files:
			fn = os.path.join (path, i)
			if os.path.isdir(fn):
				stack.append( (fn, dep - 1))
	return r

if __name__=='__main__':
	if platform.system().startswith('Win'):
		conn_split=';'
	else:
		conn_split=':'

	tool_path = os.path.dirname(sys.argv[0])
	if tool_path == '':
		tool_path = os.getcwd()
	s_paths.append(tool_path)
	s_env_paths = os.getenv('PATH').split(conn_split)

	file_count = 0
	file_list = []
	for i in sys.argv[1:]:
		if i=='--scan' or i=='--autoconf':
			imake_scan_path('.')
			sys.exit(0)
		if i=='--all':
			imake_all('.')
			sys.exit(0)
		elif i=='--create-vidl':
			imake_create_vidl(sys.argv[2:])
			sys.exit(0)
		elif i=='--create-impl':
			imake_create_impl(sys.argv[2:])
			sys.exit(0)
		elif i=='--install':
			imake_install()
			sys.exit(0)
		elif i=='--help' or i=='/?' or i=='/help':
			imake_show_help()
			sys.exit(0)
		elif i.startswith('--'):
			file_list.append(i)
		else:
			file_list.append(i)
			file_count += 1

	if file_count == 0:
		file_list.append('.')

	conf = imake_conf()
	conf.open_list(file_list, None)	
	for i in conf:
		print "-- %s\n" % i
	conf.process()
